package org.intelligentsia.temporal;

import java.io.Serializable;

import com.google.common.base.Preconditions;

/**
 * TransactionTime.
 * 
 * 
 * We use long time representation and primitive in order to lower GC.
 * 
 * @author <a href="mailto:jguibert@intelligents-ia.com" >Jerome Guibert</a>
 * 
 */
public class TransactionTime implements Serializable, Comparable<TransactionTime> {

	private static final long	serialVersionUID	= -112504200637178823L;
	/**
	 * start transaction time.
	 */
	long						start;
	/**
	 * end transaction time.
	 */
	long						end;

	/**
	 * Build a new TransactionTime instance with [Long.MIN_VALUE,
	 * Long.MAX_VALUE].
	 */
	public TransactionTime() {
		this(Long.MIN_VALUE, Long.MAX_VALUE);
	}

	/**
	 * Copy constructor.
	 * 
	 * @param transactionTime
	 *            original
	 * @throws IllegalArgumentException
	 *             if original transaction time is null
	 */
	public TransactionTime(final TransactionTime transactionTime) throws IllegalArgumentException {
		super();
		Preconditions.checkNotNull(transactionTime, "transaction time source cannot be null");
		start = transactionTime.start;
		end = transactionTime.end;
	}

	/**
	 * Build a new TransactionTime instance with [startTime, startTime].
	 * 
	 * @param startTime
	 *            start Time
	 */
	public TransactionTime(final long startTime) throws IllegalArgumentException {
		super();
		start = startTime;
		end = start;
	}

	/**
	 * Build a new TransactionTime instance with [start, end].
	 * 
	 * @param start
	 * @param end
	 * @throws IllegalStateException
	 *             if start > end
	 */
	public TransactionTime(final long start, final long end) throws IllegalStateException {
		super();
		Preconditions.checkState(start <= end, "start must be lesser or equals to end time");
		this.start = start;
		this.end = end;

	}

	/**
	 * @param value
	 * @return True if value is in [start, end]
	 */
	public boolean contains(final Long value) {
		return (start <= value) && (end >= value);
	}

	/**
	 * @param value
	 * @return True if value is in ]start, end[
	 */
	public boolean containStrictly(final Long value) {
		return (start < value) && (end > value);
	}

	/**
	 * Compute intersection of a with specified value.
	 * 
	 * @param a
	 * @param value
	 *            must be strictly in a
	 * @return upper interval of validity if exist or null if none exists. a
	 *         will be updated.
	 * 
	 * @throws IllegalArgumentException
	 *             if value is equals to Long.MIN_VALUE
	 */
	public static TransactionTime intersect(final TransactionTime a, final long value) throws IllegalArgumentException {
		Preconditions.checkArgument((Long.MIN_VALUE != value), "value cannot be Long.MIN_VALUE ");
		TransactionTime result = null;
		if (a.duration() > 1) {
			if (a.containStrictly(value)) {
				result = new TransactionTime(value, a.end);
				a.end = value - 1L;
			}
		}
		return result;
	}

	/**
	 * @param b
	 * @return true if b intersect this.
	 */
	public boolean intersects(final TransactionTime b) {
		return ((b.start <= end) && (b.start >= start)) || ((start <= b.end) && (start >= b.start));
	}

	/**
	 * Compare this and other. Return 0 if this in other, 1 if this.end >
	 * other.end with other.start < this.start, -1 if this.start < other.start,
	 * and this.end < other.end.
	 * 
	 * Its translate relation before/after.
	 * 
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(final TransactionTime other) {
		if (start >= other.start) {
			if (end <= other.end) {
				return 0; // include case
			}
			return 1;
		}
		if (end >= other.end) {
			return 0; // include case
		}
		return -1;
	}

	/**
	 * @return duration of TransactionTime. If <0, duration cannot be compute
	 *         (deal with max and min long value).
	 */
	public long duration() {
		return end - start;
	}

	@Override
	public String toString() {
		return "{start:" + start + ", end:" + end + "}";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (end ^ (end >>> 32));
		result = prime * result + (int) (start ^ (start >>> 32));
		return result;
	}

	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final TransactionTime other = (TransactionTime) obj;
		if (end != other.end) {
			return false;
		}
		if (start != other.start) {
			return false;
		}
		return true;
	}

}
